/**
 * @file OutputCommandImpl.java
 * Projet eCOM 2010 (Miage Descartes)
 * @author Doido Sébastien
 * @version 2
 * @brief Shell : Gestion des fonctionnalités relatives aux sorties
 */
package ecom.shell;

import ecom.shell.ShellConstantes.RESULT_ACTION;
import java.io.PrintStream;
import java.util.StringTokenizer;
import org.jdom.Attribute;
import org.jdom.Document;
import org.jdom.Element;
import org.jdom.output.Format;
import org.jdom.output.XMLOutputter;

/**
 * @brief Shell : Gestion des fonctionnalités relatives aux sorties
 * @author Sebastien
 */
public class OutputCommandImpl implements ShellCommand {

    private ShellContext _context = null;

    public enum Outputs {

        TEXT("text/plain"), XML("text/xml"), HTML("text/html"), JSON("application/json");
        private String name;

        Outputs(String n) {
            this.name = n;
        }

        public static Outputs getDefault() {
            return XML;
        }

        public static Outputs get(String s) throws UnknownArg {
            for (Outputs o : Outputs.values()) {
                if (s.equals(String.valueOf(o))) {
                    return o;
                }
            }
            throw new UnknownArg("Error : " + s + " is an unknown argument");
        }

        public String getName() {
            return this.name;
        }
    };

    public OutputCommandImpl(ShellContext context) {
        _context = context;
    }

    /**
     * @brief Renvoie le nom de la commande
     * @return le nom de la commande
     */
    public String getName() {
        return "output";
    }

    /**
     * @brief Renvoie la manière d'utiliser la commande
     * @return la manière d'utiliser la commande
     */
    public String getUsage() {
        StringBuffer sb = new StringBuffer();

        Outputs[] v = Outputs.values();

        for (int i = 0; i < v.length; ++i) {
            sb.append(v[i]);
            if (i < v.length - 1) {
                sb.append("|");
            }
        }
        return "output " + sb;
    }

    /**
     * @brief Renvoie la description de la commande
     * @return la description de la commande
     */
    public String getShortDescription() {
        return "Set output format\n";
    }

    /**
     * @brief Exécute la commande
     * @param cmdline : commande à exécuter
     * @param out : flux d'affichage std
     * @param err : flux d'affichage err
     * @return les données à afficher
     * @throws java.lang.Exception : erreur durant l'exécution de la méthode
     */
    public String execute(String cmdline, PrintStream out, PrintStream err) {
        StringBuffer res = new StringBuffer();
        StringTokenizer st = new StringTokenizer(cmdline, " ");

        // Ignore cmd
        st.nextToken();

        // list all known languages
        if (st.countTokens() == 0) {
            try {
                Outputs[] symbols = Outputs.values();
                Outputs currentOutput = (Outputs) _context.getVar("output");
                return printList(out, symbols, currentOutput);
            } catch (Exception ex) {
                err.println("Error with currencies display");
            }
        } else if (st.countTokens() == 1) {
            String newOutput = st.nextToken().trim();
            Outputs o;
            try {
                o = Outputs.get(newOutput);
                res.append(_context.returnCode("New Output : " + String.valueOf(o) + " - " + o.getName(), RESULT_ACTION.SUCCESS));
                _context.setVar("output", o);

            } catch (UnknownArg ex) {
                res.append(_context.returnCode(ex + " for " + getName() + " command", RESULT_ACTION.FAILED));
                err.println(ex + " for " + getName() + " command");
            }
        } else {
            res.append(_context.returnCode(Shell.INCORRECT_ARGS_NUMBER, RESULT_ACTION.FAILED));
                    err.println("\t" + Shell.INCORRECT_ARGS_NUMBER);
        }
        return res.toString();
    }

    /**
     * @brief Affichage de la liste des sorties disponibles
     * @param out : flux d'affichage std
     * @param symbols : liste des sorties disponibles
     * @param currentOutput : type de sortie attendue
     * @return les données à afficher selon le type de sortie attendue
     */
    private String printList(PrintStream out, Outputs[] symbols, Outputs currentOutput) {
        StringBuffer res = new StringBuffer();
        if (currentOutput.equals(Outputs.XML)) {
            Element outputs = new Element("outputs");
            org.jdom.Document document = new Document(outputs);

            for (int i = 0; i < symbols.length; ++i) {
                outputs.addContent(buildXMLNode(symbols[i]));
            }
            XMLOutputter sortie = new XMLOutputter(Format.getPrettyFormat());
            res.append(sortie.outputString(document));
        } else if (currentOutput.equals(Outputs.TEXT)) {
            res.append("#################" + "\n");
            res.append("# Known outputs #" + "\n");
            res.append("#################" + "\n");
            for (int i = 0; i < symbols.length; ++i) {
                res.append("\t" + String.valueOf(symbols[i]) + " - " + symbols[i].getName() + "\n");
            }
        } else {
            res.append("\t /!\\ Output format '" + currentOutput + "' is known but not implementing for this command");
        }
        return res.toString();
    }

    /**
     * @brief Construit un arbre XML pour la sortie à afficher
     * @param o : sortie
     * @return un arbre XML décrivant la sortie à afficher
     */
    private Element buildXMLNode(Outputs o) {
        Element language = new Element("output");

        Element name = new Element("name");
        Attribute id = new Attribute("id", String.valueOf(o));
        name.setAttribute(id);
        name.setText(o.getName());
        language.addContent(name);

        return language;
    }
}
